package pr.iceworld.fernando.api.oms.gateway.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import pr.iceworld.fernando.entity.Permission;
import pr.iceworld.fernando.entity.Role;
import pr.iceworld.fernando.entity.User;
import pr.iceworld.fernando.oms.service.IShiroService;
import pr.iceworld.fernando.oms.service.IUserService;
import pr.iceworld.fernando.oms.service.PasswordHelper;
import pr.iceworld.fernando.oms.util.SessionUtils;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Slf4j
public class UserRealm extends AuthorizingRealm {

    /**
     * 由于在ShiroConfiguration中使用了new UserRealm()无参构造器，无法注入IShiroService，本处使用成员属性上@Autowired
     */
    @Resource
    private IShiroService shiroService;
    @Resource
    private IUserService userService;
    @Resource
    PasswordHelper passwordHelper;

    /**
     * 查询权限，授权
     * 此方法调用hasRole,hasPermission的时候才会进行回调.
     * <p>
     * 权限信息.(授权):
     * 1、如果用户正常退出，缓存自动清空；
     * 2、如果用户非正常退出，缓存自动清空；
     * 3、如果我们修改了用户的权限，而用户不退出系统，修改的权限无法立即生效。
     * （需要手动编程进行实现；放在service进行调用）
     * 在权限修改后调用realm中的方法，realm已经由spring管理，所以从spring中获取realm实例，调用clearCached方法；
     * :Authorization 是授权访问控制，用于对用户进行的操作授权，证明该用户是否允许进行当前操作，如访问某个链接，某个资源文件等。
     *
     * @param principalCollection 身份集合
     * @return 授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("UserRealm.doGetAuthorizationInfo() shiro authentication");

        // 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout() (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，需要清除身份
        if (!SecurityUtils.getSubject().isAuthenticated()) {
            doClearCache(principalCollection);
            SecurityUtils.getSubject().logout();
            return null;
        }

//        String currentLoginName = (String) principalCollection.getPrimaryPrincipal();
//        List<String> userRoles = new ArrayList<>();
//        List<String> userPermissions = new ArrayList<>();


        // 简单授权信息
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = SessionUtils.getCurrentUser();
        if (user != null) {
            Set<String> roleNames = new HashSet<>();
            List<Role> roles = user.getRoles();
            for (Role role : roles) {
                roleNames.add(role.getName());
            }
            //添加角色
            authorizationInfo.addRoles(roleNames);

            Set<String> permissionNames = new HashSet<>();
            List<Permission> permissions = user.getPermissions();
            for (Permission permission : permissions) {
                permissionNames.add(permission.getName());
            }
            // 添加权限
            authorizationInfo.addStringPermissions(permissionNames);
        }

        return authorizationInfo;
    }

    /**
     * 校验权限,认证
     *
     * @param authenticationToken 认证Token，只是用户密码认证(UsernamePasswordToken)/ID密码认证(IdPasswordToken)
     * @return 认证信息
     * @throws AuthenticationException 认证异常
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("UserRealm.doGetAuthenticationInfo() shiro authentication");
        AuthenticationInfo authenticationInfo = null;
        User user = null;
        if (authenticationToken instanceof UsernamePasswordToken) {
            log.info("Use UsernamePasswordToken for authentication");
            //用户密码校验
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            //user = shiroService.login(null, token.getUsername(), null, String.valueOf(token.getPassword()));
            User param = new User();
            param.setUsername(token.getUsername());
            user = userService.getOne(param);
            if (user != null) {
                User userxx = new User();
                userxx.setUsername(token.getUsername());
                userxx.setPassword(new String(token.getPassword()));
                passwordHelper.encryptPassword(userxx);
                System.out.println("authenticationToken = [" + userxx.getPassword() + "]");
                authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getCredentialsSalt()), this.getName());
            }
        }

        if (user != null) {
            SessionUtils.setAttribute(SessionUtils.CURRENT_USER, user);
        }

        return authenticationInfo;
    }



    @Override
    public boolean supports(AuthenticationToken token) {
        // 支持用户名/密码、用户ID/密码的认证类型
        boolean support = token instanceof UsernamePasswordToken || token instanceof IdPasswordToken;
        return token != null && support;
    }


    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }
}
